﻿/*
 * Copyright 2007-2008  Lukasz Laszko lukaszlaszko@gmail.com
 * 
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for any purpose and without fee is hereby
 * granted, provided that the above copyright notice appear in all
 * copies and that both that the copyright notice and this
 * permission notice and warranty disclaimer appear in supporting
 * documentation, and that the name of the author not be used in
 * advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 * 
 * The author disclaim all warranties with regard to this
 * software, including all implied warranties of merchantability
 * and fitness.  In no event shall the author be liable for any
 * special, indirect or consequential damages or any damages
 * whatsoever resulting from loss of use, data or profits, whether
 * in an action of contract, negligence or other tortious action,
 * arising out of or in connection with the use or performance of
 * this software. 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Web;
using System.Web.UI;

namespace Pg.BioMedics.SDR.Web.Controls
{
    /// <summary>
    /// Custom IHTTPModule supporting file upload
    /// </summary>
    public class FileUploadModule : IHttpModule
    {
        #region Constants

        internal const string UploadFormPath = "uploadForm.ashx";
        internal const string UploadPath = "uploadFile.ashx";
        internal const string UploadStatus = "uploadStatus.ashx";

        private const string UploadFormActionKey = "{action}";
        private const string UploadFormUploadIdKey = "{uploadId}";

        private const string UploadIdKey = "uploadId";

        private const string FileUploadFormResource = "Pg.BioMedics.SDR.Web.Controls.FileUpload.FileUploadForm.htm";

        #endregion

        #region Event handlers

        private void context_PostAuthorizeRequest(object sender, EventArgs e)
        {
            HttpContext context = HttpContext.Current;
            if (CheckSecurityRules(context))
            {
                if (context.Request.Path.Contains(UploadFormPath)) // send upload form
                {
                    RenderUploadForm(context);
                }
                else if (context.Request.Path.EndsWith(UploadPath)) // receive uploaded data            
                {
                    HandleFileUpload(context);
                    RenderUploadForm(context);
                }
                else if (context.Request.Path.Contains(UploadStatus)) // get upload status
                {
                    ReportStatus(context);
                }
            }
        }

        #endregion

        #region IHttpModule Members

        public void Init(HttpApplication context)
        {
            context.PostAuthorizeRequest += new EventHandler(context_PostAuthorizeRequest);
        }        

        public void Dispose()
        {           
            UploadedFilesRegister.Instance.Dispose();
        }

        #endregion

        #region Support methods

        private bool CheckSecurityRules(HttpContext context)
        {
#if DEBUG
            return true;
#else
            if (context.User == null)
                return false;
            else if (!context.User.Identity.IsAuthenticated)
                return false;
            else
                return true;
#endif
        }

        private void RenderUploadForm(HttpContext context)
        {
            // get upload id
            string uploadIdString = context.Request.Params[UploadIdKey];
            Guid uploadId = new Guid(uploadIdString);

            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(FileUploadFormResource))
            {
                using (StreamReader reader = new StreamReader(stream))
                {
                    string uploadFormContent = reader.ReadToEnd();
                    uploadFormContent = uploadFormContent.Replace(UploadFormActionKey, UploadPath)
                                                         .Replace(UploadFormUploadIdKey, uploadIdString);

                    context.Response.Write(uploadFormContent);
                    context.Response.End();
                }
            }
        }

        private void HandleFileUpload(HttpContext context)
        {
            Guid uploadId = Guid.Empty;

            try
            {
                // get upload id
                string uploadIdString = context.Request.Form[UploadIdKey];
                uploadId = new Guid(uploadIdString);

                // get file info
                HttpPostedFile postedFile = context.Request.Files[0];
                UploadedFile uploadedFile = new UploadedFile() { FileName = postedFile.FileName, Length = postedFile.ContentLength };
                UploadedFilesRegister.Instance.Register(uploadId, uploadedFile);

                using (Stream inputStream = postedFile.InputStream)
                {
                    using (Stream outputStream = File.OpenWrite(uploadedFile.Location))
                    {
                        byte[] buffer = new byte[8192];
                        int len = 0;
                        while ((len = inputStream.Read(buffer, 0, 8192)) > 0)
                        {
                            outputStream.Write(buffer, 0, len);
                            uploadedFile.Position += len; 
                        }
                    }
                }
            }
            catch
            {
                UploadedFilesRegister.Instance.Unregister(uploadId);
                throw;
            }  
        }

        private void ReportStatus(HttpContext context)
        {
            try
            {
                // get upload id
                string uploadIdString = context.Request.Params[UploadIdKey];
                Guid uploadId = new Guid(uploadIdString);

                // get upload progress
                UploadedFile uploadedFile = UploadedFilesRegister.Instance[uploadId];

                StringBuilder report = new StringBuilder("{");
                report.AppendFormat("uploadId : '{0}', position : {1}, length : {2}", uploadId, uploadedFile.Position, uploadedFile.Length);
                report.Append("}");

                context.Response.ClearHeaders();
                context.Response.Write(report.ToString());
                context.Response.End();
            }
            catch (Exception ex)
            {
                //context.Response.End();
            }
        }

        #endregion
    }
}
